﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Media;
using System.Windows;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;


namespace Face_Recognition
{
    class Filters
    {
        int scale;
        public Bitmap bitmap;
        public Bitmap tempBitmap;
        bool[,] skinMask;
        private Clustering clustering;
        private RegionDetection regionDetection;

        public Filters(Bitmap _bitmap)
        {
            bitmap = _bitmap;
           //scale = (int)Math.Round((double)((bitmap.Height + bitmap.Width) / 320));
            scale = 2;
            //skinMask = new bool[bitmap.Height, bitmap.Width];
            tempBitmap = DeepClone<Bitmap>(bitmap);
        }

        public Rectangle FindFace()
        {
            Rectangle rect = new Rectangle();
            //rect = FindFaceBoudingBox(Morphology.FindHoles(FilterSkin(), bitmap));
            return rect;
            //tempBitmap = DeepClone<Bitmap>(bitmap);
        }

        public List<Rectangle> FindFacesViaHoles()
        {
            //skinMask= ColorFilters.GetColorsMask(bitmap);
            //clustering = new Clustering(Morphology.FindHoles(skinMask, bitmap),skinMask);
 
            return clustering.FindFaces();
        }

        public List<Rectangle> FindFacesViaRegions()
        {
            List<bool[,]> masks = new List<bool[,]>();

            bitmap = this.ResizeBitmap(bitmap, (double)(1.0/Settings.Scaling));

            masks = ColorFilters.GetColorsMask(bitmap);
            regionDetection = new RegionDetection(Morphology.FindSkinRegions(masks, bitmap), skinMask);

            List<Rectangle> rects = new List<Rectangle>();

   

               foreach (Rectangle r in regionDetection.FindFaces())
               {
                   rects.Add( new Rectangle((int)(r.X * Settings.Scaling), (int)(r.Y * Settings.Scaling),(int)(r.Width * Settings.Scaling),(int)( r.Height * Settings.Scaling)) );
               }


            return rects;
        }

        private int[] RGBToYCbCr(int R, int G, int B)
        {
            int Y = (int)(0.299 * R + 0.587 * G + 0.114 * B +16);
            int Cb = (int)(-0.16874 * R - 0.33126 * G + 0.50000 * B + 128);
            int Cr = (int)(0.50000 * R - 0.41869 * G - 0.08131 * B + 128);

            return new int[3] { Y,Cb,Cr} ;

        }
        private double[] RGBToIRgBy(int R, int G, int B)
        {
            double I = (LogOpponent(R) + LogOpponent(G) + LogOpponent(B)) / 3;
            double Rg = LogOpponent(R) - LogOpponent(G);
            double By = LogOpponent(B) - (LogOpponent(G) + LogOpponent(B)) / 2;
            return new double[3] {I,Rg,By};
        }

        private double [] RgByToHueSaturation(double Rg, double By)
        {
            double hue = Math.Pow(Math.Atan2(Rg,By),2);
            double saturation = Math.Sqrt(Math.Pow(Rg,2) + Math.Pow(By,2));

            return new double[2] { hue, saturation };


        }

        private Bitmap ResizeBitmap(Bitmap source, double factor)
        {
            Bitmap result = new Bitmap((int)(source.Width * factor), (int)(source.Height*factor));
            using (Graphics g = Graphics.FromImage(result))
                g.DrawImage(source, 0, 0, (int)(source.Width * factor), (int)(source.Height * factor));
            return result;
        }

        private double LogOpponent(int x)
        {
            return 105 * Math.Log10(x + 1);
        }

        public static void ColorToHSV(Color color, out double hue, out double saturation, out double value)
        {
            int max = Math.Max(color.R, Math.Max(color.G, color.B));
            int min = Math.Min(color.R, Math.Min(color.G, color.B));

            hue = color.GetHue();
            saturation = (max == 0) ? 0 : 1d - (1d * min / max);
            value = max / 255d;
        }

        private bool[,] GetYCbCrMask()
        {
            bool[,] mask = new bool[bitmap.Height, bitmap.Width];
            int[] YCbRb = new int[3];
            for (int w = scale; w < bitmap.Width - scale; w++)
            {
                for (int h = scale; h < bitmap.Height - scale; h++)
                {
                    YCbRb = RGBToYCbCr(bitmap.GetPixel(w, h).R, bitmap.GetPixel(w, h).G, bitmap.GetPixel(w, h).B);
                    if (YCbRb[2] > 130 && YCbRb[2] < 170 && YCbRb[1] > 75 && YCbRb[1] < 130)
                        mask[h, w] = true;
                    else
                        mask[h, w] = false;
                }
            }
            
            return mask;
        }

        private bool[,] GetHSVMask()
        {
            bool[,] mask = new bool[bitmap.Height, bitmap.Width];
            System.Drawing.Color color;
            for (int w = scale; w < bitmap.Width - scale; w++)
            {
                for (int h = scale; h < bitmap.Height - scale; h++)
                {
                    color = Color.FromArgb(bitmap.GetPixel(w, h).R,bitmap.GetPixel(w, h).G,bitmap.GetPixel(w, h).B);
                    if ((color.GetHue() < 40 && color.GetHue() > 0 || color.GetHue() < 370 && color.GetHue() > 325) && color.GetSaturation() > 0.2)
                        mask[h, w] = true;
                    else
                        mask[h, w] = false;
                }
            }

            return mask;
        }

        private bool[,] GetRGBMask()
        {
            bool[,] mask = new bool[bitmap.Height, bitmap.Width];
            for (int w = scale; w < bitmap.Width - scale; w++)
            {
                for (int h = scale; h < bitmap.Height - scale; h++)
                {

                    if (bitmap.GetPixel(w, h).R > 90 && bitmap.GetPixel(w, h).G > 30 && bitmap.GetPixel(w, h).B > 20 && bitmap.GetPixel(w, h).R > bitmap.GetPixel(w, h).B 
                        && bitmap.GetPixel(w, h).R > bitmap.GetPixel(w, h).G && Math.Abs(bitmap.GetPixel(w, h).R - bitmap.GetPixel(w, h).G) > 10)
                        mask[h, w] = true;
                    else
                        mask[h, w] = false;
                }
            }

            return mask;
        }

        private bool[,] Dilatation(bool [,] image, int scale)
        {
            bool[,] mask = new bool[bitmap.Height/2, bitmap.Width/2];
            int range = scale / 2;

            for (int w = scale; w < bitmap.Width/2 - scale; w++)
            {
                for (int h = scale; h < bitmap.Height/2 - scale; h++)
                {
                    if (image[h, w] == true)
                    {
                        for (int i = -scale/2; i < scale/2; i++)
                        {
                            for (int j = -scale/2; j < scale/2; j++)
                            {
                                if (Math.Abs(i + j) <= range) 
                                mask[h + j, w + i] = true;
                            }
                        }
                    }
                }
            }
            return mask;
        }

       // private Queue<int[]> holes = new Queue<int[]>();

        public static T DeepClone<T>(T obj)
        {
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;

                return (T)formatter.Deserialize(ms);
            }
        }

        private bool[,] Erosion(bool[,] image, int scale)
        {

            bool[,] mask = DeepClone<bool[,]>(image);
            int range = scale / 2;

            for (int w = scale; w < bitmap.Width/2 - scale; w++)
            {
                for (int h = scale; h < bitmap.Height/2 - scale; h++)
                {
                    if (image[h, w] == false)
                    {
                        for (int i = -scale / 2; i < scale / 2; i++)
                        {
                            for (int j = -scale / 2; j < scale / 2; j++)
                            {
                                if (Math.Abs(i + j) <= range) 
                                mask[h + j, w + i] = false;
                                //holes.Enqueue(new int[2] { h + j, w + i });
                            }
                        }
                    }
                }
            }

            return mask;
        }

        private Rectangle FindFaceBoudingBox(Queue<int[]> holes)
        {
            if (holes.Count > 0)
            {
                int xmin, xmax, ymin, ymax;
                byte counter=0;
                int[] point = new int[2];
                int[] oldPoint = new int[2];
                point = holes.Dequeue();
                xmin = point[0];
                xmax = point[0];
                ymin = point[1];
                ymax = point[1];

                while (holes.Count > 0)
                {
                    point = holes.Dequeue();
                    if (point[0] > xmax)
                        xmax = point[0];
                    if (point[0] < xmin)
                        xmin = point[0];
                    if (point[1] > ymax)
                        ymax = point[1];
                    if (point[1] < ymin)
                        ymin = point[1];

                }
                return new Rectangle(2*xmin, 2*ymin, 2*(xmax - xmin), 2*(ymax - ymin));
            }
            else return new Rectangle(0, 0, 0, 0);
        }

        public bool[,] Sobel()
        {
            short[,] xMatrix = new short[3, 3] { {-1,0,1}, {-2,0,2}, {-1,0,1} };
            short[,] yMatrix = new short[3, 3] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };
            short Xvalue = 0 ;
            short Yvalue=0;
            short XYvalue=0;
            short[,] grayscaleImage = new short[bitmap.Height, bitmap.Width];
            bool[,] mask = new bool[bitmap.Height, bitmap.Width];
            

            for (int w = 3; w < bitmap.Width - 3; w++)
            {
                for (int h = 3; h < bitmap.Height - 3; h++)
                {
                    grayscaleImage[h, w] = (short)(0.21 * tempBitmap.GetPixel(w, h).R + 0.71 * tempBitmap.GetPixel(w, h).G + 0.07 * tempBitmap.GetPixel(w, h).B);
                }
            }

            for (int w = 3; w < bitmap.Width - 3; w++)
            {
                for (int h = 3; h < bitmap.Height - 3; h++)
                {
                    Xvalue = 0;
                    Yvalue = 0;
                    XYvalue =0;
                    for (int i = -1; i < 2; i++)
                    {
                        for (int j = -1 ; j < 2; j++)
                        {
                            Xvalue += (short)(grayscaleImage[h+i,w+j] * xMatrix[i+1,j+1]);
                            Yvalue += (short)(grayscaleImage[h + i, w + j] * yMatrix[i + 1, j + 1]);
                        }
                    }

                    XYvalue = (short)Math.Sqrt(Math.Pow(Xvalue, 2) + Math.Pow(Yvalue, 2));
                    if (/*(short)(XYvalue) > 0 && */ (short)(XYvalue) < 100)
                        //grayscaleImage[h, w] = (short)(Xvalue);
                        //tempBitmap.SetPixel(w,h, Color.FromArgb((short)(XYvalue),(short)(XYvalue),(short)(XYvalue)));
                        mask[h, w] = true;
                    else
                        mask[h, w] = false;
                       // grayscaleImage[h, w] = 255;
                        //tempBitmap.SetPixel(w,h, Color.FromArgb(255,255,255));
                   // else
                       // grayscaleImage[h, w] = 0;
                      //  tempBitmap.SetPixel(w, h, Color.FromArgb(0,0,0));
                    
                    // tempBitmap.SetPixel(w,h, Color.FromArgb(grayscaleImage[h, w],grayscaleImage[h, w],grayscaleImage[h, w]));

                }
            }

            return mask;
        }

        private bool[,] Shrink(bool[,] skinMask)
        {
            bool[,] mask = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] tex = new bool[2, 2];
            short texCount;
            for (int w = 0; w < bitmap.Width - 2; w+=2)
            {
                for (int h = 0; h < bitmap.Height - 2; h+=2)
                {
                    tex = new bool[2,2] {{false,false},{false,false}};
                    texCount = 0;
                    for (int i = 0; i < 3; i++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            if (skinMask[h + i, w + j] == true)
                                texCount++;
                        }
                    }

                    if (texCount > 3)
                        mask[h / 2, w / 2] = true;
                   
                }
            }
            return mask;
        }

        public Queue<int[]> FindHoles(bool[,] skinMask)
        {
        {
        
            //
            bool[,] morphMask = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask1 = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask2 = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask3 = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask4 = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask5 = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask6 = new bool[bitmap.Height/2, bitmap.Width/2];
            bool[,] morphMask7 = new bool[bitmap.Height / 2, bitmap.Width / 2];
            //

            //morphMask = Dilatation(Shrink(),3);
            morphMask1 = Dilatation(Shrink(skinMask), 2);
            morphMask2 = Erosion(morphMask1, 2);
            morphMask3 = Dilatation(morphMask2,4);
            morphMask4 = Erosion(morphMask3, 4);
            morphMask5 = Dilatation(morphMask4, 6);
            morphMask6 = Erosion(morphMask5, 6);
            //morphMask3 = Dilatation(morphMask2, 6);
            //morphMask4 = Erosion(morphMask3, 6);

            Queue<int[]> holes = new Queue<int[]>();

            for (int w = 10; w < bitmap.Width/2 - 10; w++)
            {
                for (int h = 10; h < bitmap.Height/2 - 10; h++)
                {
                    if (morphMask4[h,w] == false && morphMask6[h, w] == true)
                    {
                        //holes.Enqueue(new int[2] { w, h });
                        morphMask[h, w] = true;
                        //bitmap.SetPixel(w, h, Color.White);
                    }
                    else
                    {
                        morphMask[h, w] = false;
                        //bitmap.SetPixel(w, h, Color.Black);
                    }
                }
            }

            morphMask7 = Erosion(morphMask, 3);

            for (int w = 10; w < bitmap.Width / 2 - 10; w++)
            {
                for (int h = 10; h < bitmap.Height / 2 - 10; h++)
                {
                    if (morphMask7[h, w] == true)
                    {
                        holes.Enqueue(new int[2] { w, h });
                        morphMask[h, w] = true;
                        bitmap.SetPixel(w, h, Color.White);
                    }
                    else
                    {
                        morphMask[h, w] = false;
                        bitmap.SetPixel(w, h, Color.Black);
                    }
                }
            }

            //morphMask1 = Erosion(morphMask, 3);



            return holes;

            //return bitmap;
        }
        }

        public bool[,] FilterSkin()
        {
            bool[,] maskColor = ColorFilters.GetColorsMask(bitmap)[0];
            //bool[,] maskTexture = TextureCheck();
          //  bool[,] maskYColor = GetYCbCrMask();
            bool[,] maskSobel = TextureFilters.Sobel(bitmap);
           // bool[,] maskHSV = GetHSVMask();
            bool[,] skinMask = new bool[bitmap.Height,bitmap.Width];
            //textureMask = maskTexture;

            short check = 0;

            for (int w = scale; w < bitmap.Width - scale; w++)
            {
                for (int h = scale; h < bitmap.Height - scale; h++)
                {
                    check = 0;
                    if (maskColor[h, w]) check +=1;
                    if (maskSobel[h, w]) check +=1;

                    if (check == 2)
                    {
                        skinMask[h, w] = true;
                    }
                    else
                    {
                        skinMask[h, w] = false;
                    }
                }
            }
            return skinMask;
        }      
    }
}
